﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.DirectoryServices;
    using System.Diagnostics;
    using System.Threading;
    #endregion

    [Serializable]
    public class OrganizationalUnit : ADBaseObjectProperties, IOrganizationalUnit, IGroupPolicyAttributes {

        #region Members
        #region Fields
        public static new readonly string[] AttributesToGet;

        public static readonly string ObjectCategoryShortName = "organizationalUnit"; 
        #endregion

        public IReadOnlyList<string> BusinessCategory { get; private set; }

        public IBusinessInfo BusinessInfo { get; private set; }

        public bool IsTopLevel {
            get {
                if (!string.IsNullOrWhiteSpace(this.DN)) {
                    string path = this.DN;
                    DomainConfiguration domainInfo = ADDataAccess.ADDomainInfo
                        .Where(x => x.NetbiosName == this.Domain)
                        .FirstOrDefault();

                    if (domainInfo != null) {
                        path = path.Replace("," + domainInfo.DistinguishedName, string.Empty);
                        if (!string.IsNullOrWhiteSpace(path)) {
                            if (path.StartsWith("OU=", StringComparison.OrdinalIgnoreCase)) {
                                path = path.Substring("OU=".Length);
                                if (path == this.Name) {
                                    isTopLevel = true;
                                }
                            }
                        }
                    }
                }

                return isTopLevel;
            }
        }
        protected bool isTopLevel;

        public ILocationAttributes LocationAttributes { get; private set; }

        public IReadOnlyList<string> OU { get; private set; }

        public string TelephoneNumber { get; private set; }

        #region IGroupPolicyAttributes properties
        public string GPLink { get; private set; }
        public IReadOnlyList<string> GPLinkGPODNs { get; private set; }
        public string GPOptions { get; private set; }
        #endregion

        #endregion

        #region Constructors
        static OrganizationalUnit() {
            var attributesToGet = new List<string>();
            attributesToGet.AddRange(new string[]
            {
                "businessCategory",
                "gpLink",
                "gpOptions",
                "l",
                "ou",
                "telephoneNumber"
            });

            attributesToGet.AddRange(ADBaseObjectProperties.AttributesToGet);
            attributesToGet.AddRange(Contoso.ActiveDirectory.LocationAttributes.AttributesToGet);

            AttributesToGet = attributesToGet.Distinct().ToArray();
            attributesToGet = null;
        }
        public OrganizationalUnit() {
            this.Initialize();
        }

        public OrganizationalUnit(DirectoryEntry directoryEntry, bool destroyInputObject = true)
            : base(directoryEntry, destroyInputObject) {
                if (this.GetType().Name == typeof(OrganizationalUnit).Name) {
                this.Attributes = null;
            }
        }

        public OrganizationalUnit(SearchResult result, bool destroyInputObject = true)
            : base(result, destroyInputObject) {
                if (this.GetType().Name == typeof(OrganizationalUnit).Name) {
                this.Attributes = null;
            }
        }

        public OrganizationalUnit(List<string> ldifOutput)
            : base(ldifOutput) {
                if (this.GetType().Name == typeof(OrganizationalUnit).Name) {
                this.Attributes = null;
            }
        }
        #endregion

        #region Methods
        protected override void Initialize() {
            base.Initialize();

            // IGroupPolicyAttributes
            this.GPLink = string.Empty;
            this.GPLinkGPODNs = new List<string>();
            this.GPOptions = string.Empty;

            this.BusinessCategory = new List<string>();
            this.BusinessInfo = new BusinessInfo();
            this.LocationAttributes = new LocationAttributes();
            this.OU = new List<string>();
            this.TelephoneNumber = string.Empty;
        }

        protected override void ProcessResults() {

            try {
                base.ProcessResults();

                if (this.Attributes.ContainsKey("businessCategory")) this.BusinessCategory = ADDataAccess.GetMultiValuedAttribute<string>("businessCategory", this.Attributes);
                if (this.Attributes.ContainsKey("telephoneNumber")) this.TelephoneNumber = this.Attributes["telephoneNumber"][0].ToString();

                if (this.Attributes.ContainsKey("gpLink")) this.GPLink = this.Attributes["gpLink"][0].ToString();
                if (this.Attributes.ContainsKey("gpOptions")) this.GPOptions = this.Attributes["gpOptions"][0].ToString();
                this.GPLinkGPODNs = ADDataAccess.GetGPLinkGPODNs(this.GPLink);

                if (!string.IsNullOrWhiteSpace(this.GPLink) && this.GPLinkGPODNs.Count == 0) {
                    Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} DN: {3} Warning: gpLink may contain invalid data: {4}",
                        DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId,
                        CustomExtensions.CurrentMethodName(), this.DN, this.GPLink));
                }

                if (this.Attributes.ContainsKey("ou")) this.OU = ADDataAccess.GetMultiValuedAttribute<string>("ou", this.Attributes);

                this.BusinessInfo = new BusinessInfo(this.CustomAttributes.MIExtensionAttribute2);

                this.LocationAttributes.ProcessResults(this.Attributes);

                this.IsValid = !string.IsNullOrWhiteSpace(this.DN)
                    && this.ObjectClass.Any(x => x.Equals(ObjectCategoryShortName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception e) {
                Debug.WriteLine(string.Format("{0}  [ThreadID: {1}]  {2} Error: {3}", 
                    DateTime.Now.TimeOfDay, Thread.CurrentThread.ManagedThreadId, CustomExtensions.CurrentMethodName(), e.VerboseExceptionString()));
                throw;
            }
        }
        public override string ToString() {

            var info = new StringBuilder();
            info.AppendFormat("Description: {0}; ", this.Description ?? "NULL");
            if ((this.BusinessCategory.Count == 1) && !string.IsNullOrWhiteSpace(this.BusinessCategory[0])) {
                info.AppendFormat("BusinessCategory: {0}; ", this.BusinessCategory[0]);
            }

            info.Append(this.LocationAttributes.ToString());

            return info.ToString();

        }
        #endregion
    }
}
